package com.edev.support.ddd;

import com.edev.support.dao.BasicDao;
import com.edev.support.dao.impl.DecoratorDao;
import com.edev.support.ddd.join.JoinHelper;
import com.edev.support.ddd.join.RefHelper;
import com.edev.support.entity.Entity;
import com.edev.support.utils.SpringHelper;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 领域驱动设计的通用仓库基类（DDD仓储模式核心实现）
 * <p>
 * 核心功能：
 * 1. 聚合根的生命周期管理（增删改查）
 * 2. 关联实体的事务性操作
 * 3. 关联数据的延迟加载与批量装配
 * <p>
 * 设计特性：
 * - 继承装饰器模式扩展基础DAO功能
 * - 自动处理聚合对象间的关联关系
 * - 线程安全的延迟加载控制
 */
public class Repository extends DecoratorDao {
    @Autowired
    private SpringHelper springHelper;

    /**
     * 构造方法（通过基础DAO装饰）
     * @param dao 用于真正访问数据库的基础DAO对象（支持装饰器模式）
     */
    public Repository(@NonNull BasicDao dao) {
        super(dao);
    }
    
    /**
     * 向数据库插入实体的同时，级联插入有聚合关系的关联实体，并保证事务一致性
     * <p>
     * 插入逻辑：
     * 1. 插入主实体
     * 2. 遍历主实体的所有聚合关系
     * 3. 为每个有聚合关系的关联实体进行联级插入操作
     * 4. 保证实体与关联实体的事务一致性
     * <p>
     * 示例：
     * <pre>
     * Order order = new Order(1001L);
     * order.addItem(new OrderItem(1L));
     * repository.insert(order); // 自动插入订单及其关联的订单明细
     * </pre>
     * @param entity 待插入的领域实体
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     * @return 实体主键
     */
    @Override
    @Transactional
    public <E extends Entity<S>, S extends Serializable> S insert(@NonNull E entity) {
        S id = super.insert(entity);
        JoinHelper<E,S> joinHelper = new JoinHelper<>(getDao());
        if(joinHelper.hasJoinAndAggregation(entity))
            joinHelper.insertJoins(entity);
        return id;
    }

    /**
     * 级联更新有聚合关系的关联实体
     * <p>
     * 更新逻辑：
     * 1. 遍历实体的所有聚合关系
     * 2. 为每个有聚合关系的关联实体进行联级更新操作
     * 3. 保证实体与关联实体的事务一致性
     * @param entity 待更新的领域实体
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     */
    private <E extends Entity<S>, S extends Serializable> void updateWithJoin(@NonNull E entity) {
        JoinHelper<E,S> joinHelper = new JoinHelper<>(getDao());
        if(joinHelper.hasJoinAndAggregation(entity))
            joinHelper.updateJoins(entity);
    }

    
    /**
     * 向数据库更新实体的同时，级联更新有聚合关系的关联实体，并保证事务一致性
     * <p>
     * 更新逻辑：
     * 1. 更新实体基础字段
     * 2. 对比有聚合关系的关联实体的变化
     * 3. 根据变化对关联实体进行增删改操作
     * 4. 保证实体与关联实体的事务一致性
     * @param entity 待更新的领域实体
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     */
    @Override
    @Transactional
    public <E extends Entity<S>, S extends Serializable> void update(@NonNull E entity) {
        super.update(entity);
        updateWithJoin(entity);
    }

    /**
     * 插入或更新实体的同时，级联插入或更新有聚合关系的关联实体，并保证事务一致性
     * <p>
     * 插入或更新逻辑：
     * 1. 先尝试插入实体，如果主键冲突则执行更新操作
     * 2. 执行相应的操作的同时，处理有聚合关系的关联实体
     *    - 对于新增的关联实体，执行插入操作
     *    - 对于已存在的关联实体，执行更新操作
     *    - 对于已删除的关联实体，执行删除操作
     * 3. 保证实体与关联实体的事务一致性
     * @param entity 待插入或更新的实体对象
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     * @return 实体的主键
     */
    @Override
    @Transactional
    public <E extends Entity<S>, S extends Serializable> S insertOrUpdate(@NonNull E entity) {
        S id = super.insertOrUpdate(entity);
        updateWithJoin(entity);
        return id;
    }

    /**
     * 级联删除有聚合关系的关联实体
     * <p>
     * 删除逻辑：
     * 1. 遍历实体的所有聚合关系
     * 2. 为每个有聚合关系的关联实体进行联级删除操作
     * 3. 保证实体与关联实体的事务一致性
     * @param entity 待删除的领域实体
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     */
    private <E extends Entity<S>, S extends Serializable> void deleteWithJoin(@NonNull E entity) {
        JoinHelper<E,S> joinHelper = new JoinHelper<>(getDao());
        if(joinHelper.hasJoinAndAggregation(entity))
            joinHelper.deleteJoins(entity);
    }
    
    /**
     * 根据条件模板，从数据库中删除实体的同时，级联删除有聚合关系的所有关联实体，并保证事务一致性
     * <p>
     * 示例：
     * <pre>
     * Order template = new Order();
     * template.setStatus("CANCELED");
     * repository.delete(template); // 删除所有取消的订单及其关联项
     * </pre>
     * @param template 删除条件模板
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     */
    @Override
    @Transactional
    public <E extends Entity<S>, S extends Serializable> void delete(@NonNull E template) {
        Collection<E> entities = super.loadAll(template);
        entities.forEach(this::deleteWithJoin);
        super.delete(template);
    }

    /**
     * 批量插入或更新实体列表的同时，级联操作有聚合关系的关联实体，并保证事务一致性
     * <p>
     * 插入或更新逻辑：
     * 1. 遍历实体列表，为每个实体执行插入或更新操作
     * 2. 处理有聚合关系的关联实体
     *    - 对于新增的关联实体，执行插入操作
     *    - 对于已存在的关联实体，执行更新操作
     *    - 对于已删除的关联实体，执行删除操作
     * 3. 保证实体与关联实体的事务一致性
     * @param list 待插入或更新的实体列表
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     */
    @Override
    @Transactional
    public <E extends Entity<S>, S extends Serializable, C extends Collection<E>>
            void insertOrUpdateForList(@NonNull C list) {
        list.forEach(this::insertOrUpdate);
    }

    /**
     * 批量删除实体列表的同时，级联删除有聚合关系的所有关联实体，并保证事务一致性
     * <p>
     * 删除逻辑：
     * 1. 遍历实体列表，为每个实体执行删除操作
     * 2. 对于每个实体，删除所有有聚合关系的关联实体
     * 3. 保证实体与关联实体的事务一致性
     * @param list 待删除的实体列表
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     */
    @Override
    @Transactional
    public <E extends Entity<S>, S extends Serializable, C extends Collection<E>>
        void deleteForList(@NonNull C list) {
        list.forEach(this::delete);
    }

    /**
     * 根据主键列表批量删除实体列表的同时，级联删除有聚合关系的所有关联实体，并保证事务一致性
     * <p>
     * 删除逻辑：
     * 1. 遍历实体列表，为每个实体执行删除操作
     * 2. 对于每个实体，删除所有有聚合关系的关联实体
     * 3. 保证实体与关联实体的事务一致性
     * @param ids 待删除的实体主键列表
     * @param clazz 实体类类型
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     */
    @Override
    @Transactional
    public <E extends Entity<S>, S extends Serializable>
        void deleteForList(@NonNull Collection<S> ids, @NonNull Class<E> clazz) {
        ids.forEach(id->delete(id,clazz));
    }

    /**
     * 根据主键删除实体的同时，级联删除有聚合关系的所有关联实体，并保证事务一致性
     * <p>
     * 删除逻辑：
     * 1. 加载实体对象，以便查找并删除所有有聚合关系的关联实体
     * 2. 再执行删除操作，删除主实体
     * 3. 保证实体与关联实体的事务一致性
     * @param id 待删除的实体主键
     * @param clazz 实体类类型
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     */
    @Override
    @Transactional
    public <E extends Entity<S>, S extends Serializable>
            void delete(@NonNull S id, @NonNull Class<E> clazz) {
        JoinHelper<E,S> joinHelper = new JoinHelper<>(getDao());
        if(joinHelper.hasJoinAndAggregation(clazz)) {
            E entity = this.load(id, clazz);
            if(entity!=null) deleteWithJoin(entity);
        }
        super.delete(id, clazz);
    }

    /**
     * 根据主键加载实体对象，并自动加载有联级关系的关联实体
     * <p>
     * 加载逻辑：
     * 1. 调用基础DAO的加载方法，加载主实体对象
     * 2. 如果实体对象存在关联关系，则执行关联实体的加载
     * 3. 返回加载后的实体对象
     * @param id 实体主键
     * @param clazz 实体类类型
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     * @return 加载的实体对象
     */
    @Override
    public <E extends Entity<S>, S extends Serializable>
            E load(@NonNull S id, @NonNull Class<E> clazz) {
        E entity = super.load(id, clazz);
        if(entity==null||Repository.isNotJoin()) return entity;
        (new JoinHelper<E,S>(this)).setJoins(entity);
        (new RefHelper<E,S>(springHelper)).setRefs(entity);
        return entity;
    }

    /**
     * 根据主键列表批量加载实体对象列表，并自动加载有联级关系的关联实体
     * <p>
     * 加载逻辑：
     * 1. 调用基础DAO的批量加载方法，加载主实体对象列表
     * 2. 如果实体对象存在关联关系，则为每个实体执行关联实体的加载
     * 3. 返回加载后的实体对象列表
     * @param ids 实体主键列表
     * @param clazz 实体类类型
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     * @return 完整的实体对象列表
     */
    @Override
    public <E extends Entity<S>, S extends Serializable>
            Collection<E> loadForList(@NonNull Collection<S> ids, @NonNull Class<E> clazz) {
        Collection<E> collection = super.loadForList(ids, clazz);
        if(collection==null||collection.isEmpty()||Repository.isNotJoin())
            return collection;
        (new JoinHelper<E,S>(this)).setJoinForList(collection);
        (new RefHelper<E,S>(springHelper)).setRefForList(collection);
        return collection;
    }

    /**
     * 根据条件模板加载实体对象列表，并自动加载有联级关系的关联实体
     * <p>
     * 示例代码：
     * <pre>
     * Order template = new Order();
     * template.setStatus("COMPLETED");
     * List<Order> orders = repository.loadAll(template); // 加载所有已完成的订单及其关联项
     * </pre>
     * <p>
     * 加载逻辑：
     * 1. 调用基础DAO的加载方法，加载主实体对象列表
     * 2. 如果实体对象存在关联关系，则为每个实体执行关联实体的加载
     * 3. 返回加载后的实体对象列表
     * @param template 实体条件模板
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     * @return 符合条件的实体对象列表
     */
    @Override
    public <E extends Entity<S>, S extends Serializable> Collection<E> loadAll(@NonNull E template) {
        Collection<E> collection = super.loadAll(template);
        if(collection==null||collection.isEmpty()||Repository.isNotJoin())
            return collection;
        (new JoinHelper<E,S>(this)).setJoinForList(collection);
        (new RefHelper<E,S>(springHelper)).setRefForList(collection);
        return collection;
    }

    /**
     * 根据条件模板列表加载实体对象列表，并自动加载有联级关系的关联实体
     * <p>
     * 示例代码：
     * <pre>
     * List<Map<String, Object>> conditions = new ArrayList<>();
     * Map<String, Object> condition1 = new HashMap<>();
     * condition1.put("status", "COMPLETED");
     * conditions.add(condition1);
     * Map<String, Object> condition2 = new HashMap<>();
     * condition2.put("customerId", 1L);
     * conditions.add(condition2);
     * List<Order> orders = repository.loadAll(conditions, Order.class); // 加载所有客户1已完成的订单及其关联项
     * </pre>
     * <p>
     * 加载逻辑：
     * 1. 调用基础DAO的加载方法，加载主实体对象列表
     * 2. 如果实体对象存在关联关系，则为每个实体执行关联实体的加载
     * 3. 返回加载后的实体对象列表
     * @param colMap 条件模板列表
     * @param clazz 实体类类型
     * @param <E> 实体类型（继承自Entity）
     * @param <S> 序列化的主键类型
     * @return 符合条件的实体对象列表
     */
    @Override
    public <E extends Entity<S>, S extends Serializable>
            Collection<E> loadAll(@NonNull List<Map<Object, Object>> colMap, @NonNull Class<E> clazz) {
        Collection<E> collection = super.loadAll(colMap, clazz);
        if(collection==null||collection.isEmpty()||Repository.isNotJoin())
            return collection;
        (new JoinHelper<E,S>(this)).setJoinForList(collection);
        (new RefHelper<E,S>(springHelper)).setRefForList(collection);
        return collection;
    }

    // 线程局部变量，用于控制是否进行关联实体的加载
    private static final ThreadLocal<Boolean> isNotJoin = new ThreadLocal<>();

    /**
     * 设置是否进行关联实体的加载
     * @param value true-不加载关联实体，false-加载关联实体
     */
    public static void setNotJoin(Boolean value) {
        isNotJoin.set(value);
    }

    /**
     * 获取是否进行关联实体的加载
     * @return true-不加载关联实体，false-加载关联实体
     */
    public static boolean isNotJoin() {
        if(isNotJoin.get()==null) isNotJoin.set(false);
        return isNotJoin.get();
    }

    /**
     * 清除线程局部变量，确保线程安全
     */
    public static void removeNotJoin() {
        isNotJoin.remove();
    }
}
